package site.teamo.biu.net.client.core.http_https_proxy;

import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import site.teamo.biu.net.client.configuration.ClientConfiguration;
import site.teamo.biu.net.client.controller.model.server_management.Server;
import site.teamo.biu.net.client.core.http_https_proxy.constant.HttpHttpsProxyServiceMode;
import site.teamo.biu.net.client.core.http_https_proxy.mode.DirectConnectionHandler;
import site.teamo.biu.net.client.core.http_https_proxy.mode.ProxyConnectionHandler;
import site.teamo.biu.net.client.core.http_https_proxy.mode.RuleConnectionHandler;
import site.teamo.biu.net.client.core.http_https_proxy.server.BrowserRequestListeningServer;
import site.teamo.biu.net.client.core.http_https_proxy.state.HttpHttpsProxyState;
import site.teamo.biu.net.client.service.ServerManagementService;
import site.teamo.biu.net.common.constant.ConnectionType;
import site.teamo.biu.net.common.exception.BiuNetBusinessException;
import site.teamo.biu.net.common.http_https_proxy.ConnectionState;
import site.teamo.biu.net.common.web.constant.ResponseCode;
import site.teamo.biu.net.common.web.mapper.SystemConfigMapper;
import site.teamo.biu.tool.spring.listener.ApplicationReadyListener;

import java.util.Optional;
import java.util.function.Supplier;

/**
 * @author: 爱做梦的锤子
 * @date: 2023/3/10 14:34
 */
@Service
public class HttpHttpsProxyService {

    @Resource
    private SystemConfigMapper systemConfigMapper;

    @Resource(name = "serverManagementServiceImpl")
    private ServerManagementService serverManagementService;

    @Autowired
    private ApplicationReadyListener listener;

    private ConnectionHandler connectionHandler;

    private BrowserRequestListeningServer server;

    @Getter
    private TrafficStatistics trafficStatistics;

    @PostConstruct
    public void init() {
        listener.addJob("代理服务初始化", this::restart);
    }

    public void restart() {
        Optional.ofNullable(this.server).ifPresent(BrowserRequestListeningServer::close);
        Optional.ofNullable(this.connectionHandler).ifPresent(ConnectionHandler::close);
        HttpHttpsProxyServiceMode mode = HttpHttpsProxyServiceMode.valueOf(ClientConfiguration.HTTP_HTTPS_PROXY_SERVICE_MODE.getContent(systemConfigMapper));
        this.trafficStatistics = ClientConfiguration.HTTP_HTTPS_PROXY_SERVICE_TRAFFIC_STATISTICS.getContent(systemConfigMapper, TrafficStatistics.class);
        this.connectionHandler = switch (mode) {
            case DIRECT -> new DirectConnectionHandler(trafficStatistics);
            case RULE -> new RuleConnectionHandler(trafficStatistics);
            case PROXY -> new ProxyConnectionHandler(trafficStatistics);
        };
        this.server = new BrowserRequestListeningServer(this.connectionHandler);
    }

    public HttpHttpsProxyState state() {
        int currentPort = Integer.parseInt(ClientConfiguration.HTTP_HTTPS_PROXY_SERVICE_LISTEN_PORT.getContent(systemConfigMapper));
        HttpHttpsProxyServiceMode currentMode = HttpHttpsProxyServiceMode.valueOf(ClientConfiguration.HTTP_HTTPS_PROXY_SERVICE_MODE.getContent(systemConfigMapper));
        String currentServerId = ClientConfiguration.HTTP_HTTPS_PROXY_SERVICE_USED_SERVER_ID.getContent(systemConfigMapper);
        return new HttpHttpsProxyState(currentPort, currentMode, currentServerId);
    }

    /**
     * 改变端口
     */
    public synchronized void changePort(int port) {
        if (!NetUtil.isValidPort(port)) {
            throw new BiuNetBusinessException(ResponseCode.BUSINESS_ERROR, "不合法的端口");
        }
        int currentPort = Integer.parseInt(ClientConfiguration.HTTP_HTTPS_PROXY_SERVICE_LISTEN_PORT.getContent(systemConfigMapper));
        if (currentPort == port) {
            return;
        }
        ClientConfiguration.HTTP_HTTPS_PROXY_SERVICE_LISTEN_PORT.setContent(systemConfigMapper, String.valueOf(port));
        this.restart();
    }

    public synchronized void changeMode(HttpHttpsProxyServiceMode mode, String serverId) {
        Supplier<String> serverIdCheck = () -> {
            String newServerId = serverId;
            String currentServerId = ClientConfiguration.HTTP_HTTPS_PROXY_SERVICE_USED_SERVER_ID.getContent(systemConfigMapper);
            if (StrUtil.isBlank(newServerId)) {
                newServerId = currentServerId;
            }
            if (StrUtil.isBlank(newServerId)) {
                throw new BiuNetBusinessException(ResponseCode.BUSINESS_ERROR, "未设置代理服务器");
            }
            Server proxyServer = serverManagementService.queryServer(newServerId);
            if (proxyServer == null) {
                throw new BiuNetBusinessException(ResponseCode.BUSINESS_ERROR, "代理服务器不存在或无法连接");
            }
            return newServerId;
        };
        //如果当前模式和代理服务id都一样，则不更新
        HttpHttpsProxyServiceMode currentMode = HttpHttpsProxyServiceMode.valueOf(ClientConfiguration.HTTP_HTTPS_PROXY_SERVICE_MODE.getContent(systemConfigMapper));
        String currentServerId = ClientConfiguration.HTTP_HTTPS_PROXY_SERVICE_USED_SERVER_ID.getContent(systemConfigMapper);
        if (currentMode.equals(mode)
                && (currentMode.equals(HttpHttpsProxyServiceMode.DIRECT) || currentServerId.equals(serverId))) {
            return;
        }
        switch (mode) {
            case DIRECT ->
                    ClientConfiguration.HTTP_HTTPS_PROXY_SERVICE_MODE.setContent(systemConfigMapper, HttpHttpsProxyServiceMode.DIRECT.name());
            case PROXY -> {
                ClientConfiguration.HTTP_HTTPS_PROXY_SERVICE_USED_SERVER_ID.setContent(systemConfigMapper, serverIdCheck.get());
                ClientConfiguration.HTTP_HTTPS_PROXY_SERVICE_MODE.setContent(systemConfigMapper, HttpHttpsProxyServiceMode.PROXY.name());
            }
            case RULE -> {
                ClientConfiguration.HTTP_HTTPS_PROXY_SERVICE_USED_SERVER_ID.setContent(systemConfigMapper, serverIdCheck.get());
                ClientConfiguration.HTTP_HTTPS_PROXY_SERVICE_MODE.setContent(systemConfigMapper, HttpHttpsProxyServiceMode.RULE.name());
            }
        }
        this.restart();
    }

    public ConnectionState connectionState(ConnectionType type) {
        if (type == null) {
            ConnectionState connectionState = this.connectionHandler.directConnectionState();
            connectionState.addAll(this.connectionHandler.proxyConnectionState());
            return connectionState;
        }
        return switch (type) {
            case DIRECT -> this.connectionHandler.directConnectionState();
            case PROXY -> this.connectionHandler.proxyConnectionState();
        };
    }


    public void updateCall(UpdateEvent event) {
        switch (event) {
            case CHANGE_PORT -> this.restart();
            case DELETE_RULE, UPSERT_RULE -> {
                if (this.connectionHandler instanceof RuleConnectionHandler) {
                    this.restart();
                }
            }
        }
    }

    @Scheduled(cron = "0/10 * * * * ?")
    public void saveTrafficStatistics() {
        ClientConfiguration.HTTP_HTTPS_PROXY_SERVICE_TRAFFIC_STATISTICS.setContent(systemConfigMapper, JSON.toJSONString(trafficStatistics));
    }
}
